package com.acme.bankapp.service.bank;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.acme.bankapp.domain.bank.Account;
import com.acme.bankapp.domain.bank.Bank;
import com.acme.bankapp.domain.bank.CheckingAccount;
import com.acme.bankapp.domain.bank.Client;
import com.acme.bankapp.domain.bank.ClientExistsException;
import com.acme.bankapp.domain.bank.Gender;
import com.acme.bankapp.domain.bank.NegativeArgumentValueException;
import com.acme.bankapp.domain.bank.NotEnoughFundsException;
import com.acme.bankapp.domain.bank.SavingAccount;

public class BankService {
	private static final String BALANCE = "Balance: ";
	private static final String MAX_AMOUNT_TO_WITHDRAW = "Maximum amount to withdraw: ";
	protected static String SAVE_BANK_PATH;


	public static void addClient(Bank bank, Client client) throws ClientExistsException {
		bank.addClient(client);
		saveBank(bank);
	}

	public static void printMaximumAmountToWithdraw(Bank bank) {
		System.out.println(MAX_AMOUNT_TO_WITHDRAW);
		for (Client client : bank.getClients()) {
			System.out.println(client.getClientSalutation() + " "
					+ client.getAccount().maximumAmountTowothdraw());
		}
		System.out.println();
	}

	public static void modifyBank(Bank bank) throws NotEnoughFundsException,
			NegativeArgumentValueException {
		bank.getClients()[0].getAccount().withdraw(5550);
		bank.getClients()[1].getAccount().deposit(-1);
		saveBank(bank);
	}

	public static void printBalance(Bank bank) {
		System.out.println(BALANCE);
		for (Client client : bank.getClients()) {
			System.out.println(client.toString());
		}
		System.out.println();
	}

	public static void loadFeedData(Bank bank, String path) {
		Scanner scan;
		try {
			scan = new Scanner(new File(path));
			while (scan.hasNext()) {
				Client client = parseClientFromFeed(scan.next());
				addClient(bank, client);
			}
			scan.close();
		} catch (ClientExistsException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	public static void saveBank(Bank bank) {
		if (SAVE_BANK_PATH == null)
			return;
		FileOutputStream fos = null;
		ObjectOutputStream oos = null;
		try {
			fos = new FileOutputStream(SAVE_BANK_PATH);
			oos = new ObjectOutputStream(fos);
			oos.writeObject(bank);
			oos.close();
			fos.close();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (oos != null)
					oos.close();
				if (fos != null)
					fos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}

		}
	}

	public static Bank loadBank() {
		if (SAVE_BANK_PATH == null)
			return null;
		FileInputStream fis = null;
		try {
			fis = new FileInputStream(SAVE_BANK_PATH);
			ObjectInputStream ois = new ObjectInputStream(fis);
			Bank bank = (Bank) ois.readObject();
			ois.close();
			fis.close();
			return bank;
		} catch (IOException | ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	public static Client parseClientFromFeed(final String line) {
		Client client = null;
		Account account = null;

		for (FeedParam param : FeedParam.values()) {
			Pattern pattern = Pattern.compile(param.pattern);
			Matcher matcher = pattern.matcher(line);
			if (matcher.lookingAt()) {
				param.value = matcher.group(1);
			}
		}

		Double balance = Double.valueOf(FeedParam.BALANCE.value);
		Double overdraft = Double.valueOf(FeedParam.OVERDRAFT.value);
		try {
			if ("c".equalsIgnoreCase(FeedParam.ACCOUNTTYPE.value)) {
				account = new CheckingAccount(balance, overdraft);
			} else {
				account = new SavingAccount(balance);
			}
		} catch (NegativeArgumentValueException e) {
			System.err.println("Account cant be created");
		}

		String name = FeedParam.NAME.value;
		Gender gender = "m".equalsIgnoreCase(FeedParam.GENDER.value) ? Gender.MALE
				: Gender.FEMALE;

		client = new Client(name, gender, account);
		return client;
	}

	public enum FeedParam {
		ACCOUNTTYPE(".*accounttype=(\\w*);"), BALANCE(".*balance=(\\w*);"), OVERDRAFT(
				".*overdraft=(\\w*);"), NAME(".*name=(\\w*);"), GENDER(
				".*gender=(\\w*);");
		private String pattern;
		String value;

		private FeedParam(String param) {
			this.pattern = param;
		}
	}

}
